home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / gnu / cvs-1_3.lha / cvs-1.3 / src / update.c < prev    next >
C/C++ Source or Header  |  1992-04-09  |  27KB  |  1,029 lines

  1. /*
  2.  * Copyright (c) 1992, Brian Berliner and Jeff Polk
  3.  * Copyright (c) 1989-1992, Brian Berliner
  4.  * 
  5.  * You may distribute under the terms of the GNU General Public License as
  6.  * specified in the README file that comes with the CVS 1.3 kit.
  7.  * 
  8.  * "update" updates the version in the present directory with respect to the RCS
  9.  * repository.  The present version must have been created by "checkout". The
  10.  * user can keep up-to-date by calling "update" whenever he feels like it.
  11.  * 
  12.  * The present version can be committed by "commit", but this keeps the version
  13.  * in tact.
  14.  * 
  15.  * Arguments following the options are taken to be file names to be updated,
  16.  * rather than updating the entire directory.
  17.  * 
  18.  * Modified or non-existent RCS files are checked out and reported as U
  19.  * <user_file>
  20.  * 
  21.  * Modified user files are reported as M <user_file>.  If both the RCS file and
  22.  * the user file have been modified, the user file is replaced by the result
  23.  * of rcsmerge, and a backup file is written for the user in .#file.version.
  24.  * If this throws up irreconcilable differences, the file is reported as C
  25.  * <user_file>, and as M <user_file> otherwise.
  26.  * 
  27.  * Files added but not yet committed are reported as A <user_file>. Files
  28.  * removed but not yet committed are reported as R <user_file>.
  29.  * 
  30.  * If the current directory contains subdirectories that hold concurrent
  31.  * versions, these are updated too.  If the -d option was specified, new
  32.  * directories added to the repository are automatically created and updated
  33.  * as well.
  34.  */
  35.  
  36. #include "cvs.h"
  37.  
  38. #ifndef lint
  39. static char rcsid[] = "@(#)update.c 1.83 92/04/10";
  40. #endif
  41.  
  42. #if __STDC__
  43. static int checkout_file (char *file, char *repository, List *entries,
  44.               List *srcfiles, Vers_TS *vers_ts, char *update_dir);
  45. static int isemptydir (char *dir);
  46. static int merge_file (char *file, char *repository, List *entries,
  47.                Vers_TS *vers, char *update_dir);
  48. static int scratch_file (char *file, char *repository, List * entries,
  49.              char *update_dir);
  50. static Dtype update_dirent_proc (char *dir, char *repository, char *update_dir);
  51. static int update_dirleave_proc (char *dir, int err, char *update_dir);
  52. static int update_file_proc (char *file, char *update_dir, char *repository,
  53.                  List * entries, List * srcfiles);
  54. static int update_filesdone_proc (int err, char *repository, char *update_dir);
  55. static int write_letter (char *file, int letter, char *update_dir);
  56. static void ignore_files (List * ilist, char *update_dir);
  57. static void join_file (char *file, List *srcfiles, Vers_TS *vers_ts,
  58.                char *update_dir);
  59. #else
  60. static int update_file_proc ();
  61. static int update_filesdone_proc ();
  62. static Dtype update_dirent_proc ();
  63. static int update_dirleave_proc ();
  64. static int isemptydir ();
  65. static int scratch_file ();
  66. static int checkout_file ();
  67. static int write_letter ();
  68. static int merge_file ();
  69. static void ignore_files ();
  70. static void join_file ();
  71. #endif                /* __STDC__ */
  72.  
  73. static char *options = NULL;
  74. static char *tag = NULL;
  75. static char *date = NULL;
  76. static char *join_rev1, *date_rev1;
  77. static char *join_rev2, *date_rev2;
  78. static int aflag = 0;
  79. static int force_tag_match = 1;
  80. static int update_build_dirs = 0;
  81. static int update_prune_dirs = 0;
  82. static int pipeout = 0;
  83. static List *ignlist = (List *) NULL;
  84.  
  85. static char *update_usage[] =
  86. {
  87.     "Usage:\n %s %s [-APQdflRpq] [-k kopt] [-r rev|-D date] [-j rev] [-I ign] [files...]\n",
  88.     "\t-A\tReset any sticky tags/date/kopts.\n",
  89.     "\t-P\tPrune empty directories.\n",
  90.     "\t-Q\tReally quiet.\n",
  91.     "\t-d\tBuild directories, like checkout does.\n",
  92.     "\t-f\tForce a head revision match if tag/date not found.\n",
  93.     "\t-l\tLocal directory only, no recursion.\n",
  94.     "\t-R\tProcess directories recursively.\n",
  95.     "\t-p\tSend updates to standard output.\n",
  96.     "\t-q\tSomewhat quiet.\n",
  97.     "\t-k kopt\tUse RCS kopt -k option on checkout.\n",
  98.     "\t-r rev\tUpdate using specified revision/tag.\n",
  99.     "\t-D date\tSet date to update from.\n",
  100.     "\t-j rev\tMerge in changes made between current revision and rev.\n",
  101.     "\t-I ign\tMore files to ignore (! to reset).\n",
  102.     NULL
  103. };
  104.  
  105. /*
  106.  * update is the argv,argc based front end for arg parsing
  107.  */
  108. int
  109. update (argc, argv)
  110.     int argc;
  111.     char *argv[];
  112. {
  113.     int c, err;
  114.     int local = 0;            /* recursive by default */
  115.     int which;                /* where to look for files and dirs */
  116.  
  117.     if (argc == -1)
  118.     usage (update_usage);
  119.  
  120.     ign_setup ();
  121.  
  122.     /* parse the args */
  123.     optind = 1;
  124.     while ((c = gnu_getopt (argc, argv, "ApPflRQqdk:r:D:j:I:")) != -1)
  125.     {
  126.     switch (c)
  127.     {
  128.         case 'A':
  129.         aflag = 1;
  130.         break;
  131.         case 'I':
  132.         ign_add (optarg, 0);
  133.         break;
  134.         case 'k':
  135.         if (options)
  136.             free (options);
  137.         options = RCS_check_kflag (optarg);
  138.         break;
  139.         case 'l':
  140.         local = 1;
  141.         break;
  142.         case 'R':
  143.         local = 0;
  144.         break;
  145.         case 'Q':
  146.         really_quiet = 1;
  147.         /* FALL THROUGH */
  148.         case 'q':
  149.         quiet = 1;
  150.         break;
  151.         case 'd':
  152.         update_build_dirs = 1;
  153.         break;
  154.         case 'f':
  155.         force_tag_match = 0;
  156.         break;
  157.         case 'r':
  158.         tag = optarg;
  159.         break;
  160.         case 'D':
  161.         date = Make_Date (optarg);
  162.         break;
  163.         case 'P':
  164.         update_prune_dirs = 1;
  165.         break;
  166.         case 'p':
  167.         pipeout = 1;
  168.         noexec = 1;        /* so no locks will be created */
  169.         break;
  170.         case 'j':
  171.         if (join_rev2)
  172.             error (1, 0, "only two -j options can be specified");
  173.         if (join_rev1)
  174.             join_rev2 = optarg;
  175.         else
  176.             join_rev1 = optarg;
  177.         break;
  178.         case '?':
  179.         default:
  180.         usage (update_usage);
  181.         break;
  182.     }
  183.     }
  184.     argc -= optind;
  185.     argv += optind;
  186.  
  187.     /*
  188.      * If we are updating the entire directory (for real) and building dirs
  189.      * as we go, we make sure there is no static entries file and write the
  190.      * tag file as appropriate
  191.      */
  192.     if (argc <= 0 && !pipeout)
  193.     {
  194.     if (update_build_dirs)
  195.         (void) unlink_file (CVSADM_ENTSTAT);
  196.  
  197.     /* keep the CVS/Tag file current with the specified arguments */
  198.     if (aflag || tag || date)
  199.         WriteTag ((char *) NULL, tag, date);
  200.     }
  201.  
  202.     /* look for files/dirs locally and in the repository */
  203.     which = W_LOCAL | W_REPOS;
  204.  
  205.     /* look in the attic too if a tag or date is specified */
  206.     if (tag != NULL || date != NULL)
  207.     which |= W_ATTIC;
  208.  
  209.     /* call the command line interface */
  210.     err = do_update (argc, argv, options, tag, date, force_tag_match,
  211.              local, update_build_dirs, aflag, update_prune_dirs,
  212.              pipeout, which, join_rev1, join_rev2, (char *) NULL);
  213.  
  214.     /* free the space Make_Date allocated if necessary */
  215.     if (date != NULL)
  216.     free (date);
  217.  
  218.     return (err);
  219. }
  220.  
  221. /*
  222.  * Command line interface to update (used by checkout)
  223.  */
  224. int
  225. do_update (argc, argv, xoptions, xtag, xdate, xforce, local, xbuild, xaflag,
  226.        xprune, xpipeout, which, xjoin_rev1, xjoin_rev2, preload_update_dir)
  227.     int argc;
  228.     char *argv[];
  229.     char *xoptions;
  230.     char *xtag;
  231.     char *xdate;
  232.     int xforce;
  233.     int local;
  234.     int xbuild;
  235.     int xaflag;
  236.     int xprune;
  237.     int xpipeout;
  238.     int which;
  239.     char *xjoin_rev1;
  240.     char *xjoin_rev2;
  241.     char *preload_update_dir;
  242. {
  243.     int err = 0;
  244.     char *cp;
  245.  
  246.     /* fill in the statics */
  247.     options = xoptions;
  248.     tag = xtag;
  249.     date = xdate;
  250.     force_tag_match = xforce;
  251.     update_build_dirs = xbuild;
  252.     aflag = xaflag;
  253.     update_prune_dirs = xprune;
  254.     pipeout = xpipeout;
  255.  
  256.     /* setup the join support */
  257.     join_rev1 = xjoin_rev1;
  258.     join_rev2 = xjoin_rev2;
  259.     if (join_rev1 && (cp = index (join_rev1, ':')) != NULL)
  260.     {
  261.     *cp++ = '\0';
  262.     date_rev1 = Make_Date (cp);
  263.     }
  264.     else
  265.     date_rev1 = (char *) NULL;
  266.     if (join_rev2 && (cp = index (join_rev2, ':')) != NULL)
  267.     {
  268.     *cp++ = '\0';
  269.     date_rev2 = Make_Date (cp);
  270.     }
  271.     else
  272.     date_rev2 = (char *) NULL;
  273.  
  274.     /* call the recursion processor */
  275.     err = start_recursion (update_file_proc, update_filesdone_proc,
  276.                update_dirent_proc, update_dirleave_proc,
  277.                argc, argv, local, which, aflag, 1,
  278.                preload_update_dir, 1);
  279.     return (err);
  280. }
  281.  
  282. /*
  283.  * This is the callback proc for update.  It is called for each file in each
  284.  * directory by the recursion code.  The current directory is the local
  285.  * instantiation.  file is the file name we are to operate on. update_dir is
  286.  * set to the path relative to where we started (for pretty printing).
  287.  * repository is the repository. entries and srcfiles are the pre-parsed
  288.  * entries and source control files.
  289.  * 
  290.  * This routine decides what needs to be done for each file and does the
  291.  * appropriate magic for checkout
  292.  */
  293. static int
  294. update_file_proc (file, update_dir, repository, entries, srcfiles)
  295.     char *file;
  296.     char *update_dir;
  297.     char *repository;
  298.     List *entries;
  299.     List *srcfiles;
  300. {
  301.     int retval;
  302.     Ctype status;
  303.     Vers_TS *vers;
  304.  
  305.     status = Classify_File (file, tag, date, options, force_tag_match,
  306.                 aflag, repository, entries, srcfiles, &vers);
  307.     if (pipeout)
  308.     {
  309.     /*
  310.      * We just return success without doing anything if any of the really
  311.      * funky cases occur
  312.      * 
  313.      * If there is still a valid RCS file, do a regular checkout type
  314.      * operation
  315.      */
  316.     switch (status)
  317.     {
  318.         case T_UNKNOWN:        /* unknown file was explicitly asked
  319.                      * about */
  320.         case T_REMOVE_ENTRY:    /* needs to be un-registered */
  321.         case T_ADDED:        /* added but not committed */
  322.         retval = 0;
  323.         break;
  324.         case T_CONFLICT:        /* old punt-type errors */
  325.         retval = 1;
  326.         break;
  327.         case T_UPTODATE:        /* file was already up-to-date */
  328.         case T_NEEDS_MERGE:        /* needs merging */
  329.         case T_MODIFIED:        /* locally modified */
  330.         case T_REMOVED:        /* removed but not committed */
  331.         case T_CHECKOUT:        /* needs checkout */
  332.         retval = checkout_file (file, repository, entries, srcfiles,
  333.                     vers, update_dir);
  334.         break;
  335.  
  336.         default:            /* can't ever happen :-) */
  337.         error (0, 0,
  338.                "unknown file status %d for file %s", status, file);
  339.         retval = 0;
  340.         break;
  341.     }
  342.     }
  343.     else
  344.     {
  345.     switch (status)
  346.     {
  347.         case T_UNKNOWN:        /* unknown file was explicitly asked
  348.                      * about */
  349.         case T_UPTODATE:        /* file was already up-to-date */
  350.         retval = 0;
  351.         break;
  352.         case T_CONFLICT:        /* old punt-type errors */
  353.         retval = 1;
  354.         break;
  355.         case T_NEEDS_MERGE:    /* needs merging */
  356.         retval = merge_file (file, repository, entries,
  357.                      vers, update_dir);
  358.         break;
  359.         case T_MODIFIED:        /* locally modified */
  360.         retval = write_letter (file, 'M', update_dir);
  361.         break;
  362.         case T_CHECKOUT:        /* needs checkout */
  363.         retval = checkout_file (file, repository, entries, srcfiles,
  364.                     vers, update_dir);
  365.         break;
  366.         case T_ADDED:        /* added but not committed */
  367.         retval = write_letter (file, 'A', update_dir);
  368.         break;
  369.         case T_REMOVED:        /* removed but not committed */
  370.         retval = write_letter (file, 'R', update_dir);
  371.         break;
  372.         case T_REMOVE_ENTRY:    /* needs to be un-registered */
  373.         retval = scratch_file (file, repository, entries, update_dir);
  374.         break;
  375.         default:            /* can't ever happen :-) */
  376.         error (0, 0,
  377.                "unknown file status %d for file %s", status, file);
  378.         retval = 0;
  379.         break;
  380.     }
  381.     }
  382.  
  383.     /* only try to join if things have gone well thus far */
  384.     if (retval == 0 && join_rev1)
  385.     join_file (file, srcfiles, vers, update_dir);
  386.  
  387.     /* if this directory has an ignore list, add this file to it */
  388.     if (ignlist)
  389.     {
  390.     Node *p;
  391.  
  392.     p = getnode ();
  393.     p->type = FILES;
  394.     p->key = xstrdup (file);
  395.     (void) addnode (ignlist, p);
  396.     }
  397.  
  398.     freevers_ts (&vers);
  399.     return (retval);
  400. }
  401.  
  402. /*
  403.  * update_filesdone_proc () is used
  404.  */
  405. /* ARGSUSED */
  406. static int
  407. update_filesdone_proc (err, repository, update_dir)
  408.     int err;
  409.     char *repository;
  410.     char *update_dir;
  411. {
  412.     /* if this directory has an ignore list, process it then free it */
  413.     if (ignlist)
  414.     {
  415.     ignore_files (ignlist, update_dir);
  416.     dellist (&ignlist);
  417.     }
  418.  
  419.     /* Clean up CVS admin dirs if we are export */
  420.     if (strcmp (command_name, "export") == 0)
  421.     {
  422.     run_setup ("%s -fr", RM);
  423.     run_arg (CVSADM);
  424.     (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  425.     }
  426.  
  427.     return (err);
  428. }
  429.  
  430. /*
  431.  * update_dirent_proc () is called back by the recursion processor before a
  432.  * sub-directory is processed for update.  In this case, update_dirent proc
  433.  * will probably create the directory unless -d isn't specified and this is a
  434.  * new directory.  A return code of 0 indicates the directory should be
  435.  * processed by the recursion code.  A return of non-zero indicates the
  436.  * recursion code should skip this directory.
  437.  */
  438. static Dtype
  439. update_dirent_proc (dir, repository, update_dir)
  440.     char *dir;
  441.     char *repository;
  442.     char *update_dir;
  443. {
  444.     if (!isdir (dir))
  445.     {
  446.     /* if we aren't building dirs, blow it off */
  447.     if (!update_build_dirs)
  448.         return (R_SKIP_ALL);
  449.  
  450.     if (noexec)
  451.     {
  452.         /* ignore the missing dir if -n is specified */
  453.         error (0, 0, "New directory `%s' -- ignored", dir);
  454.         return (R_SKIP_ALL);
  455.     }
  456.     else
  457.     {
  458.         /* otherwise, create the dir and appropriate adm files */
  459.         make_directory (dir);
  460.         Create_Admin (dir, repository, tag, date);
  461.     }
  462.     }
  463.  
  464.     /*
  465.      * If we are building dirs and not going to stdout, we make sure there is
  466.      * no static entries file and write the tag file as appropriate
  467.      */
  468.     if (!pipeout)
  469.     {
  470.     if (update_build_dirs)
  471.     {
  472.         char tmp[PATH_MAX];
  473.  
  474.         (void) sprintf (tmp, "%s/%s", dir, CVSADM_ENTSTAT);
  475.         (void) unlink_file (tmp);
  476.     }
  477.  
  478.     /* keep the CVS/Tag file current with the specified arguments */
  479.     if (aflag || tag || date)
  480.         WriteTag (dir, tag, date);
  481.  
  482.     /* initialize the ignore list for this directory */
  483.     ignlist = getlist ();
  484.     }
  485.  
  486.     /* print the warm fuzzy message */
  487.     if (!quiet)
  488.     error (0, 0, "Updating %s", update_dir);
  489.  
  490.     return (R_PROCESS);
  491. }
  492.  
  493. /*
  494.  * update_dirleave_proc () is called back by the recursion code upon leaving
  495.  * a directory.  It will prune empty directories if needed and will execute
  496.  * any appropriate update programs.
  497.  */
  498. /* ARGSUSED */
  499. static int
  500. update_dirleave_proc (dir, err, update_dir)
  501.     char *dir;
  502.     int err;
  503.     char *update_dir;
  504. {
  505.     FILE *fp;
  506.  
  507.     /* run the update_prog if there is one */
  508.     if (err == 0 && !pipeout && !noexec &&
  509.     (fp = fopen (CVSADM_UPROG, "r")) != NULL)
  510.     {
  511.     char *cp;
  512.     char *repository;
  513.     char line[MAXLINELEN];
  514.  
  515.     repository = Name_Repository ((char *) NULL, update_dir);
  516.     if (fgets (line, sizeof (line), fp) != NULL)
  517.     {
  518.         if ((cp = rindex (line, '\n')) != NULL)
  519.         *cp = '\0';
  520.         run_setup ("%s %s", line, repository);
  521.         (void) printf ("%s %s: Executing '", program_name, command_name);
  522.         run_print (stdout);
  523.         (void) printf ("'\n");
  524.         (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  525.     }
  526.     (void) fclose (fp);
  527.     free (repository);
  528.     }
  529.  
  530.     /* Clean up CVS admin dirs if we are export */
  531.     if (strcmp (command_name, "export") == 0)
  532.     {
  533.     run_setup ("%s -fr", RM);
  534.     run_arg (CVSADM);
  535.     (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  536.     }
  537.  
  538.     /* Prune empty dirs on the way out - if necessary */
  539.     (void) chdir ("..");
  540.     if (update_prune_dirs && isemptydir (dir))
  541.     {
  542.     run_setup ("%s -fr", RM);
  543.     run_arg (dir);
  544.     (void) run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  545.     }
  546.  
  547.     return (err);
  548. }
  549.  
  550. /*
  551.  * Returns 1 if the argument directory is completely empty, other than the
  552.  * existence of the CVS directory entry.  Zero otherwise.
  553.  */
  554. static int
  555. isemptydir (dir)
  556.     char *dir;
  557. {
  558.     DIR *dirp;
  559.     struct direct *dp;
  560.  
  561.     if ((dirp = opendir (dir)) == NULL)
  562.     {
  563.     error (0, 0, "cannot open directory %s for empty check", dir);
  564.     return (0);
  565.     }
  566.     while ((dp = readdir (dirp)) != NULL)
  567.     {
  568.     if (strcmp (dp->d_name, ".") != 0 && strcmp (dp->d_name, "..") != 0 &&
  569.         strcmp (dp->d_name, CVSADM) != 0 &&
  570.         strcmp (dp->d_name, OCVSADM) != 0)
  571.     {
  572.         (void) closedir (dirp);
  573.         return (0);
  574.     }
  575.     }
  576.     (void) closedir (dirp);
  577.     return (1);
  578. }
  579.  
  580. /*
  581.  * scratch the Entries file entry associated with a file
  582.  */
  583. static int
  584. scratch_file (file, repository, entries, update_dir)
  585.     char *file;
  586.     char *repository;
  587.     List *entries;
  588.     char *update_dir;
  589. {
  590.     history_write ('W', update_dir, "", file, repository);
  591.     Scratch_Entry (entries, file);
  592.     (void) unlink_file (file);
  593.     return (0);
  594. }
  595.  
  596. /*
  597.  * check out a file - essentially returns the result of the fork on "co".
  598.  */
  599. static int
  600. checkout_file (file, repository, entries, srcfiles, vers_ts, update_dir)
  601.     char *file;
  602.     char *repository;
  603.     List *entries;
  604.     List *srcfiles;
  605.     Vers_TS *vers_ts;
  606.     char *update_dir;
  607. {
  608.     char backup[PATH_MAX];
  609.     int set_time, retval = 0;
  610.     int retcode = 0;
  611.  
  612.     /* don't screw with backup files if we're going to stdout */
  613.     if (!pipeout)
  614.     {
  615.     (void) sprintf (backup, "%s/%s%s", CVSADM, CVSPREFIX, file);
  616.     if (isfile (file))
  617.         rename_file (file, backup);
  618.     else
  619.         (void) unlink_file (backup);
  620.     }
  621.  
  622.     run_setup ("%s%s -q -r%s %s", Rcsbin, RCS_CO, vers_ts->vn_rcs,
  623.            vers_ts->options);
  624.  
  625.     /*
  626.      * if we are checking out to stdout, print a nice message to stderr, and
  627.      * add the -p flag to the command
  628.      */
  629.     if (pipeout)
  630.     {
  631.     run_arg ("-p");
  632.     if (!quiet)
  633.     {
  634.         (void) fprintf (stderr, "===================================================================\n");
  635.         if (update_dir[0])
  636.         (void) fprintf (stderr, "Checking out %s/%s\n",
  637.                 update_dir, file);
  638.         else
  639.         (void) fprintf (stderr, "Checking out %s\n", file);
  640.         (void) fprintf (stderr, "RCS:  %s\n", vers_ts->srcfile->path);
  641.         (void) fprintf (stderr, "VERS: %s\n", vers_ts->vn_rcs);
  642.         (void) fprintf (stderr, "***************\n");
  643.     }
  644.     }
  645.  
  646.     /* tack on the rcs and maybe the user file */
  647.     run_arg (vers_ts->srcfile->path);
  648.     if (!pipeout)
  649.     run_arg (file);
  650.  
  651.     if ((retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY,
  652.         (pipeout ? (RUN_NORMAL|RUN_REALLY) : RUN_NORMAL))) == 0)
  653.     {
  654.     if (!pipeout)
  655.     {
  656.         Vers_TS *xvers_ts;
  657.  
  658.         if (cvswrite == TRUE)
  659.         xchmod (file, 1);
  660.  
  661.         /* set the time from the RCS file iff it was unknown before */
  662.         if (vers_ts->vn_user == NULL ||
  663.         strncmp (vers_ts->ts_rcs, "Initial", 7) == 0)
  664.         {
  665.         set_time = 1;
  666.         }
  667.         else
  668.         set_time = 0;
  669.  
  670.         xvers_ts = Version_TS (repository, options, tag, date, file,
  671.                   force_tag_match, set_time, entries, srcfiles);
  672.         if (strcmp (xvers_ts->options, "-V4") == 0)
  673.         xvers_ts->options[0] = '\0';
  674.         Register (entries, file, xvers_ts->vn_rcs, xvers_ts->ts_user,
  675.               xvers_ts->options, xvers_ts->tag, xvers_ts->date);
  676.  
  677.         /* fix up the vers structure, in case it is used by join */
  678.         if (join_rev1)
  679.         {
  680.         if (vers_ts->vn_user != NULL)
  681.             free (vers_ts->vn_user);
  682.         if (vers_ts->vn_rcs != NULL)
  683.             free (vers_ts->vn_rcs);
  684.         vers_ts->vn_user = xstrdup (xvers_ts->vn_rcs);
  685.         vers_ts->vn_rcs = xstrdup (xvers_ts->vn_rcs);
  686.         }
  687.  
  688.         /* If this is really Update and not Checkout, recode history */
  689.         if (strcmp (command_name, "update") == 0)
  690.         history_write ('U', update_dir, xvers_ts->vn_rcs, file,
  691.                    repository);
  692.  
  693.         freevers_ts (&xvers_ts);
  694.  
  695.         if (!really_quiet)
  696.         {
  697.         if (update_dir[0])
  698.             (void) printf ("U %s/%s\n", update_dir, file);
  699.         else
  700.             (void) printf ("U %s\n", file);
  701.         }
  702.     }
  703.     }
  704.     else
  705.     {
  706.     int old_errno = errno;        /* save errno value over the rename */
  707.  
  708.     if (!pipeout && isfile (backup))
  709.         rename_file (backup, file);
  710.  
  711.     error (retcode == -1 ? 1 : 0, retcode == -1 ? old_errno : 0,
  712.            "could not check out %s", file);
  713.  
  714.     retval = retcode;
  715.     }
  716.  
  717.     if (!pipeout)
  718.     (void) unlink_file (backup);
  719.  
  720.     return (retval);
  721. }
  722.  
  723. /*
  724.  * Several of the types we process only print a bit of information consisting
  725.  * of a single letter and the name.
  726.  */
  727. static int
  728. write_letter (file, letter, update_dir)
  729.     char *file;
  730.     char letter;
  731.     char *update_dir;
  732. {
  733.     if (!really_quiet)
  734.     {
  735.     if (update_dir[0])
  736.         (void) printf ("%c %s/%s\n", letter, update_dir, file);
  737.     else
  738.         (void) printf ("%c %s\n", letter, file);
  739.     }
  740.     return (0);
  741. }
  742.  
  743. /*
  744.  * Do all the magic associated with a file which needs to be merged
  745.  */
  746. static int
  747. merge_file (file, repository, entries, vers, update_dir)
  748.     char *file;
  749.     char *repository;
  750.     List *entries;
  751.     Vers_TS *vers;
  752.     char *update_dir;
  753. {
  754.     char user[PATH_MAX];
  755.     char backup[PATH_MAX];
  756.     int status;
  757.     int retcode = 0;
  758.  
  759.     /*
  760.      * The users currently modified file is moved to a backup file name
  761.      * ".#filename.version", so that it will stay around for a few days
  762.      * before being automatically removed by some cron daemon.  The "version"
  763.      * is the version of the file that the user was most up-to-date with
  764.      * before the merge.
  765.      */
  766.     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, file, vers->vn_user);
  767.     if (update_dir[0])
  768.     (void) sprintf (user, "%s/%s", update_dir, file);
  769.     else
  770.     (void) strcpy (user, file);
  771.  
  772.     (void) unlink_file (backup);
  773.     copy_file (file, backup);
  774.     xchmod (file, 1);
  775.  
  776.     /* XXX - Do merge by hand instead of using rcsmerge, due to -k handling */
  777.     run_setup ("%s%s %s -r%s -r%s", Rcsbin, RCS_RCSMERGE, vers->options,
  778.            vers->vn_user, vers->vn_rcs);
  779.     run_arg (vers->srcfile->path);
  780.     status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  781.     if (status != 0
  782. #ifdef HAVE_RCS5
  783.     && status != 1
  784. #endif
  785.     )
  786.     {
  787.     error (0, status == -1 ? errno : 0,
  788.            "could not merge revision %s of %s", vers->vn_user, user);
  789.     error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
  790.            user, backup);
  791.     rename_file (backup, file);
  792.     return (1);
  793.     }
  794.     /* XXX - Might want to make sure that rcsmerge changed the file */
  795.     if (strcmp (vers->options, "-V4") == 0)
  796.     vers->options[0] = '\0';
  797.     Register (entries, file, vers->vn_rcs, vers->ts_rcs, vers->options,
  798.           vers->tag, vers->date);
  799.  
  800.     /* fix up the vers structure, in case it is used by join */
  801.     if (join_rev1)
  802.     {
  803.     if (vers->vn_user != NULL)
  804.         free (vers->vn_user);
  805.     vers->vn_user = xstrdup (vers->vn_rcs);
  806.     }
  807.  
  808.     /* possibly run GREP to see if there appear to be conflicts in the file */
  809.     run_setup ("%s -s", GREP);
  810.     run_arg (RCS_MERGE_PAT);
  811.     run_arg (file);
  812.     if (status == 1 ||
  813.     (retcode = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL)) == 0)
  814.     {
  815.     if (!noexec)
  816.         error (0, 0, "conflicts found in %s", user);
  817.  
  818.     if (!really_quiet)
  819.         (void) printf ("C %s\n", user);
  820.  
  821.     history_write ('C', update_dir, vers->vn_rcs, file, repository);
  822.  
  823.     }
  824.     else if (retcode == -1)
  825.     {
  826.     error (1, errno, "fork failed while examining update of %s", user);
  827.     }
  828.     else
  829.     {
  830.     if (!really_quiet)
  831.         (void) printf ("M %s\n", user);
  832.     history_write ('G', update_dir, vers->vn_rcs, file, repository);
  833.     }
  834.     return (0);
  835. }
  836.  
  837. /*
  838.  * Do all the magic associated with a file which needs to be joined
  839.  * (-j option)
  840.  */
  841. static void
  842. join_file (file, srcfiles, vers, update_dir)
  843.     char *file;
  844.     List *srcfiles;
  845.     Vers_TS *vers;
  846.     char *update_dir;
  847. {
  848.     char user[PATH_MAX];
  849.     char backup[PATH_MAX];
  850.     char *rev, *baserev;
  851.     char *options;
  852.     int status;
  853.  
  854.     /* determine if we need to do anything at all */
  855.     if (vers->vn_user == NULL || vers->srcfile == NULL ||
  856.     vers->srcfile->path == NULL)
  857.     {
  858.     return;
  859.     }
  860.  
  861.     /* special handling when two revisions are specified */
  862.     if (join_rev1 && join_rev2)
  863.     {
  864.     rev = RCS_getversion (vers->srcfile, join_rev2, date_rev2, 1);
  865.     if (rev == NULL)
  866.     {
  867.         if (!quiet && date_rev2 == NULL)
  868.         error (0, 0,
  869.                "cannot find revision %s in file %s", join_rev2, file);
  870.         return;
  871.     }
  872.  
  873.     baserev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1);
  874.     if (baserev == NULL)
  875.     {
  876.         if (!quiet && date_rev1 == NULL)
  877.         error (0, 0,
  878.                "cannot find revision %s in file %s", join_rev1, file);
  879.         free (rev);
  880.         return;
  881.     }
  882.  
  883.     /*
  884.      * nothing to do if:
  885.      *    second revision matches our BASE revision (vn_user) &&
  886.      *    both revisions are on the same branch
  887.      */
  888.     if (strcmp (vers->vn_user, rev) == 0 &&
  889.         numdots (baserev) == numdots (rev))
  890.     {
  891.         /* might be the same branch.  take a real look */
  892.         char *dot = rindex (baserev, '.');
  893.         int len = (dot - baserev) + 1;
  894.  
  895.         if (strncmp (baserev, rev, len) == 0)
  896.         return;
  897.     }
  898.     }
  899.     else
  900.     {
  901.     rev = RCS_getversion (vers->srcfile, join_rev1, date_rev1, 1);
  902.     if (rev == NULL)
  903.         return;
  904.     if (strcmp (rev, vers->vn_user) == 0) /* no merge necessary */
  905.     {
  906.         free (rev);
  907.         return;
  908.     }
  909.  
  910.     baserev = RCS_whatbranch (file, join_rev1, srcfiles);
  911.     if (baserev)
  912.     {
  913.         char *cp;
  914.  
  915.         /* we get a branch -- turn it into a revision, or NULL if trunk */
  916.         if ((cp = rindex (baserev, '.')) == NULL)
  917.         {
  918.         free (baserev);
  919.         baserev = (char *) NULL;
  920.         }
  921.         else
  922.         *cp = '\0';
  923.     }
  924.     }
  925.     if (baserev && strcmp (baserev, rev) == 0)
  926.     {
  927.     /* they match -> nothing to do */
  928.     free (rev);
  929.     free (baserev);
  930.     return;
  931.     }
  932.  
  933.     /* OK, so we have a revision and possibly a base revision; continue on */
  934.     
  935.     /*
  936.      * The users currently modified file is moved to a backup file name
  937.      * ".#filename.version", so that it will stay around for a few days
  938.      * before being automatically removed by some cron daemon.  The "version"
  939.      * is the version of the file that the user was most up-to-date with
  940.      * before the merge.
  941.      */
  942.     (void) sprintf (backup, "%s%s.%s", BAKPREFIX, file, vers->vn_user);
  943.     if (update_dir[0])
  944.     (void) sprintf (user, "%s/%s", update_dir, file);
  945.     else
  946.     (void) strcpy (user, file);
  947.  
  948.     (void) unlink_file (backup);
  949.     copy_file (file, backup);
  950.     xchmod (file, 1);
  951.  
  952.     options = vers->options;
  953. #ifdef HAVE_RCS5
  954.     if (*options == '\0')
  955.     options = "-kk";        /* to ignore keyword expansions */
  956. #endif
  957.  
  958.     /* XXX - Do merge by hand instead of using rcsmerge, due to -k handling */
  959.     run_setup ("%s%s %s %s%s -r%s", Rcsbin, RCS_RCSMERGE, options,
  960.            baserev ? "-r" : "", baserev ? baserev : "", rev);
  961.     run_arg (vers->srcfile->path);
  962.     status = run_exec (RUN_TTY, RUN_TTY, RUN_TTY, RUN_NORMAL);
  963.     if (status != 0
  964. #ifdef HAVE_RCS5
  965.     && status != 1
  966. #endif
  967.     )
  968.     {
  969.     error (0, status == -1 ? errno : 0,
  970.            "could not merge revision %s of %s", rev, user);
  971.     error (status == -1 ? 1 : 0, 0, "restoring %s from backup file %s",
  972.            user, backup);
  973.     rename_file (backup, file);
  974.     }
  975.     free (rev);
  976.     if (baserev)
  977.     free (baserev);
  978.     return;
  979. }
  980.  
  981. /*
  982.  * Process the current directory, looking for files not in ILIST and not on
  983.  * the global ignore list for this directory.
  984.  */
  985. static void
  986. ignore_files (ilist, update_dir)
  987.     List *ilist;
  988.     char *update_dir;
  989. {
  990.     DIR *dirp;
  991.     struct direct *dp;
  992.     struct stat sb;
  993.     char *file;
  994.     char *xdir;
  995.  
  996.     /* we get called with update_dir set to "." sometimes... strip it */
  997.     if (strcmp (update_dir, ".") == 0)
  998.     xdir = "";
  999.     else
  1000.     xdir = update_dir;
  1001.  
  1002.     dirp = opendir (".");
  1003.     if (dirp == NULL)
  1004.     return;
  1005.  
  1006.     ign_add_file (CVSDOTIGNORE, 1);
  1007.     while ((dp = readdir (dirp)) != NULL)
  1008.     {
  1009.     file = dp->d_name;
  1010.     if (strcmp (file, ".") == 0 || strcmp (file, "..") == 0)
  1011.         continue;
  1012.     if (findnode (ilist, file) != NULL)
  1013.         continue;
  1014.     if (lstat (file, &sb) != -1)
  1015.     {
  1016.         if (S_ISDIR (sb.st_mode))
  1017.         continue;
  1018. #ifdef S_IFLNK
  1019.         if (S_ISLNK (sb.st_mode))
  1020.         continue;
  1021. #endif
  1022.     }
  1023.     if (ign_name (file))
  1024.         continue;
  1025.     (void) write_letter (file, '?', xdir);
  1026.     }
  1027.     (void) closedir (dirp);
  1028. }
  1029.